// $Id: //CBT-depot/CBT++/include/SpeciesVerificationHashEntry.hh#1 $

#ifndef SPECIES_VERIFICATION_HASH_ENTRY
#define SPECIES_VERIFICATION_HASH_ENTRY

// The include list goes here.
#include <stdio.h>
#include <sstream>
#include <stdlib.h>
#include <unistd.h>
#include "FastaKMerReader.hh"
#include "HashFiller.hh"

// Constant definitions go here.
#define BASESUNUSED (4) 
#define BITSPERBASE (2UL)
#define BITSUNUSED (BASESUNUSED*BITSPERBASE)
#define BITSPERWORD (sizeof(Word) * 8UL)
#define BASEMASK ((1UL << BITSPERBASE) - 1)
#define BASESPERBYTE (8/BITSPERBASE)
#define BASESPERWORD (BITSPERWORD/BITSPERBASE)

// The class definition goes here.
class SpeciesVerificationHashEntry : virtual public HashEntry{
public:
  // The left 56 bits of this word represent the sequence.
  // Bits 57-64 are for the count.
  Word sequenceAndCount;

  inline Word getSequence(){
    return sequenceAndCount >> 8;
  }

  inline unsigned int getCount(){
    return sequenceAndCount & 0xFF;
  }

  inline static char* bases_OLD(Word w) {
    static char b[BASESPERWORD+1];
    char base = '\0';
    int basebits;
    int i;
    for (i = (BASESPERWORD - BASESUNUSED); i >= 0; i--) {
      b[i] = base;
      basebits = w & BASEMASK;
      w >>= BITSPERBASE;
      switch (basebits) {
      case A: 
	base = 'a';
	break;
      case C: 
	base = 'c';
	break;
      case G: 
	base = 'g';
	break;
      case T: 
	base = 't';
	break;
      }
    }
    return b;
  }

  inline static char* bases(Word w){
    unsigned int charHolder  =
      ('g' << (G << 3))
      | ('t' << (T << 3))
      | ('c' << (C << 3))
      | ('a' << (A << 3));
    static char b[BASESPERWORD + 1];
    char base = '\0';
    int basebits;
    int i;
    for (i = (BASESPERWORD - BASESUNUSED); i >= 0; i--) {
      b[i] = base;
      basebits = w & BASEMASK;
      w >>= BITSPERBASE;
      base = (charHolder >> (basebits << 3)) & 0xFF;
      /*if (DEBUG){
	cout << hex << "28: basebits = " << basebits << endl;
	cout << "28: basebits << 3 = " << (basebits << 3) << endl;
	cout << "28: charHolder = " << charHolder << endl;
	cout << "28: charHolder >> (basebits << 3) = ";
	cout << (charHolder >> (basebits << 3)) << endl;
	cout << "28: base = " << base << endl << dec;
	}*/
    }
    return b;
  }
      

  inline char* bases(){
    return bases(getSequence());
  }

  void incrementCount(){
    if (getCount() != 0xFF){
      // If the count is already 255, do nothing.
      sequenceAndCount += 1;  // Otherwise, increment the count.
    }
  }

  SpeciesVerificationHashEntry(){
    sequenceAndCount = 0ULL;
    arraySize = 0U;
  }

  //virtual ~Basic24MerHashEntry(){
  //}

  HashRecord integerValues(){
    return HashRecord(sequenceAndCount, NULL);
  }

  HashEntry* typedValue(HashRecord ints){
    if (ints.extensions != NULL){
      if (DEBUG){
	cout << "SVHT1:  Non-null extension array!" << endl;
      }
      return NULL;
    }
    SpeciesVerificationHashEntry* rv = new SpeciesVerificationHashEntry();
  
    if (DEBUG){
      cout << "27: C Basic24MerHashEntry.typedValue.rv " << rv << endl;
    }
    rv->sequenceAndCount = ints.primaryRecord;
    return rv;
  }
  
  string toString(){
    ostringstream oss;
    // CHANGE THIS LATER.
    oss << bases(getSequence()) << "\t" << getCount();
    return oss.str();
  }

  // Should this be redefined to parse a sequence and counts from a line?
  // Is there some behavior common across file parsers that can be made part of
  // this file?
  HashEntry* fromString(string s){
    return NULL;
  }

  // This method is not required by the HashEntry class.
  inline HashEntry* fromSequenceAndCount(Word w, int c){
    if (DEBUG){
      cout << "26: fromSequenceAndCount(" << w << ", " << c;
      cout << ")" << endl;
    }
    SpeciesVerificationHashEntry* rv = new SpeciesVerificationHashEntry();
    // Here, w = min(sequence, reverse complement).
    rv->sequenceAndCount = (w << 8) | c;
    if (DEBUG){
      cout << "26: About to return " << rv->sequenceAndCount << "..." << endl;
    }
    return rv;
  }

  // This method is not required by the HashEntry class.
  inline HashEntry* fromNewSequence(Word w){
    return fromSequenceAndCount(w, 0);
  }

  Word hashKey(){
    //Word rv = sequence;
    //rv <<= 16;
    //return rv;
    // The hash key is just the left 56 bits, which represent the sequence.
    // In the current implementation, these bits *aren't* shifted right.
    if (DEBUG){
      Word rv = sequenceAndCount & 0xFFFFFFFFFFFFFF00ULL;
      cout << "37: The hash key for " << sequenceAndCount << " is " << rv;
      cout << "." << endl;
      return rv;
    }
    return sequenceAndCount & 0xFFFFFFFFFFFFFF00ULL;
  }
  
};

#endif

